//
//  EAGLView.m
//  icamera
//
//  Created by Berlin Brown on 9/2/09.
//  Copyright berlinresearch 2009. All rights reserved.
//
//  Objective C, iPhone Development / XCode
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 1

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer     *animationTimer;

- (id)initGLES;
- (BOOL)createFramebuffer;
- (void)destroyFramebuffer;

@end // End of Interface View //

@class EAGLView;

@implementation GLViewController

- (void)actionDrawView:(EAGLView*)view {

    static GLfloat rquad;

    // Replace the implementation of this method to do your own custom drawing  
    const GLfloat quadVertices[] = {
        -1.0f,  1.0f, 0.0f,  // Point 0
         1.0f,  1.0f, 0.0f,  // Point 1
         1.0f, -1.0f, 0.0f,  // Point 2
        -1.0f, -1.0f, 0.0f   // Point 3
    };
        
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /////////////////////////////////////////////////////////////////
    
    glLoadIdentity();
    glColor4f(0.0, 0.0, 1.0, 1.0);
    glTranslatef(1.0f, 1.0f, -6.0f);
    glRotatef(rquad, 1.0f, 0.0f, 0.0f);
    glVertexPointer(3, GL_FLOAT, 0, quadVertices);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 
    glDisableClientState(GL_VERTEX_ARRAY);

    /////////////////////////////////////////////////////////////////////
    // Increment rquad 
    
    static NSTimeInterval lastDrawTime;
    if (lastDrawTime) {
        NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
        
        rquad -= (15.0 * timeSinceLastDraw); 
        if (rquad < 0.0) {
            rquad += 360.0;
        }
    } // End of if last draw //
    lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
    
    /////////////////////////////////////////////////////////////////
}

/**
 * Setup the view, camera initial position
 */
-(void)actionSetupView:(EAGLView*)view {
    
    const GLfloat zNear = 0.1, 
    zFar        = 1000.0, 
    fieldOfView = 60.0; 
    GLfloat size; 
    
    glMatrixMode(GL_PROJECTION); 
    size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
    CGRect rect = view.bounds; 
    glFrustumf(-size, size, 
               -size / (rect.size.width / rect.size.height), 
                size / (rect.size.width / rect.size.height), 
               zNear, zFar); 
    glViewport(0, 0, rect.size.width, rect.size.height); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
    
    NSLog(@"Invoking setup view, width=%d", rect.size.width);
    
} // End of Method setup view //

- (void)didReceiveMemoryWarning 
{
    [super didReceiveMemoryWarning]; 
}

- (void)dealloc 
{
    [super dealloc];
}

@end // End of Impl //


/////////////////////////////////////////////////////////////////////


@interface EAGALView (private)

- (id)initGLES;
- (BOOL)createFramebuffer;
- (void)destroyFramebuffer;

@end

@implementation EAGLView

@synthesize animationInterval;

// Implement this to override the default layer class (which is [CALayer class]).
// We do this so that our view will be backed by a layer that is capable of OpenGL ES rendering.
+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

// When created via code however, we get initWithFrame
-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if(self != nil)
    {
        self = [self initGLES];
    }
    return self;
}

//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder
{
    if((self = [super initWithCoder:coder]))
    {
        self = [self initGLES];
    }   
    return self;
}

-(id)initGLES
{
    // Get our backing layer
    CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
    
    // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
    eaglLayer.opaque = YES;
    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
                                        kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
                                        nil];
    
    // Create our EAGLContext, and if successful make it current and create our framebuffer.
    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
    if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer])
    {
        [self release];
        return nil;
    }
    
    // Default the animation interval to 1/60th of a second.
    animationInterval = 1.0 / 60.0;
    return self;
}

-(id<GLCameraViewDelegate>)delegate
{
    return delegate;
}

// Update the delegate, and if it needs a -setupView: call, set our internal flag so that it will be called.
-(void)setDelegate:(id<GLGravityViewDelegate>)d
{
    delegate = d;
    delegateSetup = ![delegate respondsToSelector:@selector(setupView:)];
}

/////////////////////////////////////////////////////////////////////

// Updates the OpenGL view when the timer fires
- (void)drawView
{
    // Make sure that you are drawing to the current context
    [EAGLContext setCurrentContext:context];
    
    // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again.
    if(!delegateSetup)
    {
        [delegate setupView:self];
        delegateSetup = YES;
    }
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

    [delegate drawView:self];
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
    
    GLenum err = glGetError();
    if (err) {
        NSLog(@"%x error", err);
    }
} // End of Draw View //

/////////////////////////////////////////////////////////////////////

// If our view is resized, we'll be asked to layout subviews.
// This is the perfect opportunity to also update the framebuffer so that it is
// the same size as our display area.
-(void)layoutSubviews
{
    [EAGLContext setCurrentContext:context];
    [self destroyFramebuffer];
    [self createFramebuffer];
    [self drawView];
}

- (BOOL)createFramebuffer
{
    // Generate IDs for a framebuffer object and a color renderbuffer
    glGenFramebuffersOES(1, &viewFramebuffer);
    glGenRenderbuffersOES(1, &viewRenderbuffer);
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
    // allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view).
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
    
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
    
    // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
    glGenRenderbuffersOES(1, &depthRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
    {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
    
    return YES;
}


// Clean up any buffers we have allocated.
- (void)destroyFramebuffer
{
    glDeleteFramebuffersOES(1, &viewFramebuffer);
    viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
    viewRenderbuffer = 0;
    
    if(depthRenderbuffer)
    {
        glDeleteRenderbuffersOES(1, &depthRenderbuffer);
        depthRenderbuffer = 0;
    }
}


/////////////////////////////////////////////////////////////////////

- (void)startAnimation
{
    animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval 
                                target:self 
                                selector:@selector(drawView) 
                                userInfo:nil 
                                repeats:YES];
}

- (void)stopAnimation
{
    [animationTimer invalidate];
    animationTimer = nil;
}

- (void)setAnimationInterval:(NSTimeInterval)interval
{
    animationInterval = interval;
    
    if(animationTimer)
    {
        [self stopAnimation];
        [self startAnimation];
    }
}

// Stop animating and release resources when they are no longer needed.
- (void)dealloc
{
    [self stopAnimation];
    
    if([EAGLContext currentContext] == context)
    {
        [EAGLContext setCurrentContext:nil];
    }
    
    [context release];
    context = nil;
    
    [super dealloc];
} // End of Method //

@end

// End of the File //
